'''
Created on Apr 24, 2009

@author: utena
'''

# file:///C|/Programming/QC Try 1/pandoras-book/scripts/default.xml

from java.awt import Dimension, BorderLayout, Font
from java.awt.event import ActionListener, KeyEvent, InputEvent, ActionEvent
from javax.jms import Message, MessageListener
from javax.swing import JFrame, JEditorPane, JScrollPane, JTextField, Timer, JButton, JTextPane, KeyStroke, JComponent
from javax.swing import JMenuBar, JMenu, JMenuItem, JSeparator, Box, JButton
from javax.swing.event import InternalFrameListener
from java.io import BufferedReader, PrintStream, InputStreamReader
from javax.swing.text import Style, StyleConstants, StyleContext, JTextComponent, DefaultEditorKit

from org.memesis.pandorasbook.ui import PandoraTextPane

import java.net.Socket as Socket
import java.net.URL as URL
import java.util.HashMap as Map
import java.util.UUID as UUID
import java.util.regex.Pattern as Pattern
import javax.swing.event.HyperlinkListener as HyperlinkListener
import javax.swing.event.HyperlinkEvent as HyperlinkEvent
try:
    import javax.jnlp.BasicService as BasicService
    import javax.jnlp.ServiceManager as ServiceManager
except: ## Must not be launching through an jnlp resource
    pass

import org.memesis.pandorasbook.util.ExceptionHandler as ExceptionHandler

class SwingTest(object):
    '''
    classdocs
    '''
    
    class ButtonListener(ActionListener):
        def __init__(self, app):
            self.app = app
            
        def actionPerformed(self, e):
            ##self.app.output.addRawText("ButtonPushed.  %s" % self.app.sock)
            if self.app.sock:
                self.app.handle_disconnect()
            else:
                self.app.connect()
    
    class URLHandler(HyperlinkListener):
        def __init__(self, app):
            self.app = app
        def hyperlinkUpdate(self, e):
            if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED):
                try:
                    self.app.bs.showDocument(e.getURL())
                except Exception, er:
                    pass
                    
            else:
                return

    class MenuAction(ActionListener):
        def __init__(self, app):
            self.app = app
            
        def actionPerformed(self, act):
            src = act.getSource()
            if src == self.app.thinkNetMenuItem:
                self.app.loadThinknetPanel()
            if src == self.app.pasteMenuItem:
                self.app.input.requestFocusInWindow()
                self.app.output.getEditorActionByName(DefaultEditorKit.pasteAction).actionPerformed(act)

    
    class RecallAction(ActionListener):
        def __init__(self, app):
            self.app = app
            
        def actionPerformed(self, act):
            self.app.curcmdhist += 1
            try:
                self.app.input.setText(self.app.commandHistory[self.app.curcmdhist])
            except:
                self.app.curcmdhist -= 1
    
    class RecallDown(ActionListener):
        def __init__(self, app):
            self.app = app
            
        def actionPerformed(self, act):
            self.app.curcmdhist -= 1
            if self.app.curcmdhist < 0:
                self.app.curcmdhist = 0
                return 
            self.app.input.setText(self.app.commandHistory[self.app.curcmdhist])

    
    class TelWindowListener(InternalFrameListener):
        def __init__(self, app):
            self.app = app
        def internalFrameClosing(self, act):
            if self.app.telnetchetimer:
                self.app.telnetchetimer.stop()
            if self.app.sock:
                self.app.sock.close()
                
        def internalFrameClosed(self, act):
            pass
        
        def internalFrameActivated(self, act):
            pass
                
        def internalFrameDeactivated(self, act):
            pass
        
        def internalFrameDeiconified(self, act):
            pass
        
        def internalFrameIconified(self, act):
            pass
        
        def internalFrameOpened(self, act):
            pass

    class SendText(ActionListener):
        def __init__(self, app, bufinput):
            self.buf = bufinput
            self.app = app
        
        def actionPerformed(self, act):
            try:
                if not self.buf.ready():
                    return
                pp = self.buf.readLine()
            except:
                self.app.handle_disconnect()
            else:
                if pp:
                    self.app.writeANSITextToScreen(pp)
                    if self.app.thinknetLoaded:
                        self.app.sendTextToThinknet(pp)
                elif pp == None:
                    self.app.handle_disconnect()
                
    class SendLine(ActionListener):
        def __init__(self, app):
            self.input = app.input
            self.outt = app.out
            self.app = app
        
        def actionPerformed(self, act):
            if self.app.sock:
                pp = self.input.getText()
                try:
                    self.outt.println(pp)
                except:
                    self.app.handle_disconnect()
                else:
                    self.input.setText("")
                    self.app.commandHistory.insert(0,pp)
                    self.app.curcmdhist = -1

    class SendLineFromMq(MessageListener):
        def __init__(self, outt):
            self.outt = outt

        def onMessage(self, m):
            self.outt.println(m.text)
            
    class WhiteboardListener(object):
        def __init__(self):
            pass
        
        def drawEvent(self, me):
            mq.publish("whiteboard", me)
            
    def sendTextToThinknet(self, te):
        global mq
        
        m = self.thinkNetFilter.matcher(te)
        if m.matches():
            channel = m.group(1).split(' ')[0]
            mq.publish(self.thinknetRecvChannel,"%s;;%s" % (channel, m.group(2)))

    def writeANSITextToScreen(self, te):
        mm = self.ansiRegEx.matcher(te)
        curstyle = self.doc.getStyle("regular")
        start = 0
        while mm.find():
            self.output.addStyledText(te[start:mm.start()], curstyle)
            codes = te[mm.start()+2:mm.end()-1]
            for code in codes.split(';'):
                if code == '0':
                    curstyle = self.doc.getStyle("regular")
                if code == '1':
                    curstyle = self.doc.getStyle("bold")
                if code == '3' or code == '5':
                    curstyle = self.doc.getStyle("italic")
                if code == '5' or code == '4':
                    curstyle = self.doc.getStyle("underline")
                if len(code) == 2 and code[0] == '3':
                    ## Color codes.   Not implemented
                    pass
            start = mm.end()
        self.output.addStyledTextLine(te[start:],curstyle)
        
        self.output.setCaretPosition(self.doc.getLength())
        self.outputScrollPane.getHorizontalScrollBar().setValue(0)
        
    def initDocumentStyles(self):
        
        deff = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
        

        ss = self.doc.addStyle("regular", deff )
        #s = Style()
        StyleConstants.setFontFamily(ss, "Courier New")
        StyleConstants.setFontSize(ss, 12)
        
        s = self.doc.addStyle("bold", ss)
        StyleConstants.setBold(s, True)
        s = self.doc.addStyle("italic", ss)
        StyleConstants.setItalic(s, True)
        s = self.doc.addStyle("underline", ss)
        StyleConstants.setUnderline(s, True)
        
    def initKeymapBindings(self):

        recallAction = SwingTest.RecallAction(self)
        recallKey = KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, False);
        self.input.registerKeyboardAction(recallAction, recallKey, JComponent.WHEN_FOCUSED);        
    
        recallDown = SwingTest.RecallDown(self)
        recallKey = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, False);
        self.input.registerKeyboardAction(recallDown, recallKey, JComponent.WHEN_FOCUSED);       
        
    def initMenuBar(self): 
        
        self.menuAction = SwingTest.MenuAction(self)
        
        self.menubar = JMenuBar()
        
        self.editMenu = JMenu("Edit")
        self.menubar.add(self.editMenu)
        
        self.cutMenuItem = JMenuItem("Cut")
        self.cutMenuItem.addActionListener(self.output.getEditorActionByName(DefaultEditorKit.cutAction))
        self.editMenu.add(self.cutMenuItem)

        self.copyMenuItem = JMenuItem("Copy")
        self.copyMenuItem.addActionListener(self.output.getEditorActionByName(DefaultEditorKit.copyAction))
        self.editMenu.add(self.copyMenuItem)

        self.pasteMenuItem = JMenuItem("Paste")
        self.pasteMenuItem.addActionListener(self.menuAction)
        self.editMenu.add(self.pasteMenuItem)

        self.windowMenu = JMenu("Windows")
        self.menubar.add(self.windowMenu)
        
        self.thinkNetMenuItem = JMenuItem("Open Thinknet Menu")
        self.thinkNetMenuItem.addActionListener(self.menuAction)
        self.windowMenu.add(self.thinkNetMenuItem)
    
    def initGUI(self, frame):
        
        ## Setup Initial Frame work
        self.frame = frame
        self.wl = SwingTest.TelWindowListener(self)
        self.frame.addInternalFrameListener(self.wl)
        
        ## Setup Output frame
        self.output = PandoraTextPane()
        self.output.setEditable(False)
        self.doc = self.output.getStyledDocument()
        self.initDocumentStyles()
        self.htlistener = SwingTest.URLHandler(self)
        self.output.addHyperlinkListener(self.htlistener)
        
        ## Setup Scroll-bars for the Output Pane
        self.outputScrollPane = JScrollPane(self.output)
        self.outputScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS)
        self.outputScrollPane.setMinimumSize(Dimension(200,200))
        self.outputScrollPane.setMaximumSize(Dimension(400,400))
        
        ##Connect / Disconnect Button
        self.button = JButton("Connect")
        self.buttonList = SwingTest.ButtonListener(self)
        self.button.addActionListener(self.buttonList)

        ## Setup Input Panel
        self.input = JTextField()
        
        ## Setup Key Bindings
        self.initKeymapBindings()
        
        ## Setup Menu
        self.initMenuBar()
        self.frame.setJMenuBar(self.menubar)
        
        self.bbox = Box.createHorizontalBox()
        self.buttonDims = Dimension(70,35)
        self.button.setMinimumSize(self.buttonDims)
        self.button.setMaximumSize(self.buttonDims)
        
        self.bbox.add(self.button)
        self.bbox.add(self.input)
        
        ## Add stuff to the Frame
        self.frame.getContentPane().add(self.outputScrollPane, BorderLayout.CENTER)
        ##self.frame.getContentPane().add(self.input, BorderLayout.PAGE_END)
        self.frame.getContentPane().add(self.bbox, BorderLayout.PAGE_END)
        ## Back the frame and maximize.  Can't just use setMaximum because of a L&F bug.
        self.frame.pack()
        self.frame.reshape(0, 0, self.frame.getDesktopPane().getWidth(), self.frame.getDesktopPane().getHeight());

    def __init__(self, frame):
        '''
        Constructor
        '''

        ## Initialize class variables.
        self.sock = None
        self.telnetchetimer = None
        self.thinknetLoaded = False
        self.commandHistory = []
        self.curcmdhist = -1

        try:
            self.bs = ServiceManager.lookup("javax.jnlp.BasicService")
        except Exception, er:
            pass
        
        
        ##  Compile the RegEx to use for ANSI support 
        self.ansiRegEx = Pattern.compile("(\x1b\[[0-9;]+m)")
        self.thinkNetFilter = Pattern.compile("^\[(.*?)\] (.*?[0-9a-zA-Z]+.*)")
        
        ## Create a GUID / UUID for this instance 
        self.instanceGUID =  UUID.randomUUID().toString()
        
        self.initGUI(frame)
        
        ## Deal with properties from applet
        if(not applet.getProperty("host") or not applet.getProperty("port")):
            self.output.setText("No 'host' or 'port' was specified for the Applet")
        else:        
            self.host = applet.getProperty("host")
            self.port = int(applet.getProperty("port"))
            
            if(not applet.getProperty("sitename")):
                self.sitename = self.host + ":" + str(self.port)
            else:
                self.sitename = applet.getProperty("sitename")  
            
            self.connect()

    def loadThinknetPanel(self):
        global applet
        global mq
        
        if not self.thinknetLoaded:
            self.thinknetChannel = "thinknet-%s" % self.instanceGUID
            self.thinknetRecvChannel = "thinknet-recv-%s" % self.instanceGUID
            
            self.mqhandle = SwingTest.SendLineFromMq(self.out)
            mq.subscribe(self.thinknetChannel, self.mqhandle)
           
            tm = Map()
            tm["jms-url"] = applet.getProperty("jms-url")
            tm["jms-channel"] = self.thinknetChannel
            tm["jms-recv-channel"] = self.thinknetRecvChannel
            applet.runApplet("PandoraMUclient/thinknet.xml", tm);
            
            self.thinknetLoaded = True
        
    def handle_disconnect(self):
        self.sock.close()
        self.sock = None
        self.telnetchetimer.stop()
        self.telnetchetimer = None
        self.button.setText("Connect")
    
    def connect(self):
        
        ## Create the socket
        try:
            self.sock = Socket(self.host,self.port)
        except Exception, e:
            print e
            return
        
        ## Setup Input and Output for the socket
        self.inn = BufferedReader(InputStreamReader(self.sock.getInputStream())) 
        self.out = PrintStream(self.sock.getOutputStream())
        self.telhandle = SwingTest.SendText(self,self.inn)
        self.outhandle = SwingTest.SendLine(self)
        self.input.addActionListener(self.outhandle)
        
        ## Setup a timer to poll the socket
        self.telnetchetimer = Timer(10, self.telhandle)
        self.telnetchetimer.start()        
        self.frame.title = "Connected to " + self.sitename

        if(applet.getProperty("initial-command")):
            cmd = applet.getProperty("initial-command")
            self.out.println(cmd)
            
        self.button.setText("Disconnect")

        


c = SwingTest(frame)
